//
//  NSObject+ADSafe.m
//  ADKit
//
//  Created by duzexu on 2017/12/8.
//

#import "NSObject+ADSafe.h"
#import "ADKitHeaders.h"
#import <objc/runtime.h>
#import <dlfcn.h>

@interface ADStubObject : NSObject

- (instancetype)init __unavailable;

+ (ADStubObject *)shareInstance;

- (BOOL)addFunc:(SEL)sel;

+ (BOOL)addClassFunc:(SEL)sel;

@end

/**
 default Implement
 
 @param target trarget
 @param cmd cmd
 @param ... other param
 @return default Implement is zero
 */
int smartFunction(id target, SEL cmd, ...) {
    return 0;
}

static BOOL _addMethod(Class clazz, SEL sel) {
    return __addMethod(clazz, sel, smartFunction);
}

@implementation ADStubObject

+ (ADStubObject *)shareInstance {
    static ADStubObject *singleton;
    if (!singleton) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            singleton = [ADStubObject new];
        });
    }
    return singleton;
}

- (BOOL)addFunc:(SEL)sel {
    return _addMethod([ADStubObject class], sel);
}

+ (BOOL)addClassFunc:(SEL)sel {
    Class metaClass = objc_getMetaClass(class_getName([ADStubObject class]));
    return _addMethod(metaClass, sel);
}

@end

@implementation NSObject (ADSafe)

#if !DEBUG
+ (void)load {
//    shield_hook_load_group(XXForOCString(ProtectFW));
//    shield_hook_load_group(XXForOCString(ProtectCont));
//    _addMethod(NSClassFromString(@"__NSArray0"), @selector(objectForKeyedSubscript:));
//    _addMethod(NSClassFromString(@"__NSSingleObjectArrayI"), @selector(objectForKeyedSubscript:));
//    _addMethod(NSClassFromString(@"__NSArrayI"), @selector(objectForKeyedSubscript:));
//    _addMethod(NSClassFromString(@"__NSArray0"), @selector(objectForKey:));
//    _addMethod(NSClassFromString(@"__NSSingleObjectArrayI"), @selector(objectForKey:));
//    _addMethod(NSClassFromString(@"__NSArrayI"), @selector(objectForKey:));
//    _addMethod(NSClassFromString(@"__NSSingleEntryDictionaryI"), @selector(objectAtIndexedSubscript:));
//    _addMethod(NSClassFromString(@"__NSDictionary0"), @selector(objectAtIndexedSubscript:));
//    _addMethod(NSClassFromString(@"__NSDictionaryI"), @selector(objectAtIndexedSubscript:));
//    _addMethod(NSClassFromString(@"__NSSingleEntryDictionaryI"), @selector(objectAtIndex:));
//    _addMethod(NSClassFromString(@"__NSDictionary0"), @selector(objectAtIndex:));
//    _addMethod(NSClassFromString(@"__NSDictionaryI"), @selector(objectAtIndex:));
}
#endif

- (void)ad_setValue:(id)value forKey:(NSString *)key {
    if (value==nil||key==nil) {
        return;
    }
    [self setValue:value forKey:key];
}

- (void)ad_setValue:(id)value forKeyPath:(NSString *)keyPath {
    if (value==nil||keyPath==nil) {
        return;
    }
    [self setValue:value forKeyPath:keyPath];
}

@end

@implementation NSMutableArray (ADSafe)

- (void)ad_addObject:(id)anObject {
    if (anObject) {
        [self addObject:anObject];
    }
}

- (void)ad_insertObject:(id)anObject atIndex:(NSUInteger)index {
    if (anObject) {
        if (index >= self.count) {
            NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@",@(index), @(self.count)];
            NSLog(@"%@",reason);
            return;
        }
        [self insertObject:anObject atIndex:index];
    }
}

- (void)ad_replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
    if (index >= self.count) {
        NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@",@(index), @(self.count)];
        NSLog(@"%@",reason);
        return;
    }
    if (anObject) {
        [self replaceObjectAtIndex:index withObject:anObject];
    }
}

- (void)ad_removeObjectAtIndex:(NSUInteger)index {
    if (index >= self.count) {
        NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@",@(index), @(self.count)];
        NSLog(@"%@",reason);
    } else {
        [self removeObjectAtIndex:index];
    }
}

- (void)ad_removeObjectsAtIndexes:(NSIndexSet *)indexes {
    NSMutableIndexSet *mutableSet = [[NSMutableIndexSet alloc] init];
    [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
        if (idx < self.count) {
            [mutableSet addIndex:idx];
        }else{
            NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@",@(idx), @(self.count)];
            NSLog(@"%@",reason);
        }
    }];
    [self removeObjectsAtIndexes:mutableSet];
}

- (void)ad_removeObjectsInRange:(NSRange)range {
    NSInteger maxIndexInRange = range.location + range.length - 1;
    if (maxIndexInRange < self.count) {
        [self removeObjectsInRange:range];
        return;
    }
    if (range.location >= self.count) {
        NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@",@(range.location), @(self.count)];
        NSLog(@"%@",reason);
        return;
    }
    while (maxIndexInRange >= self.count) {
        maxIndexInRange --;
    }
    NSRange finalRange = NSMakeRange(range.location, maxIndexInRange - range.location + 1);
    [self removeObjectsInRange:finalRange];
}

@end

@implementation NSMutableDictionary (ADSafe)

- (void)ad_setObject:(id)anObject forKey:(id<NSCopying>)aKey {
    if (anObject && aKey) {
        [self setObject:anObject forKey:aKey];
    } else {
        NSString *reason = [NSString stringWithFormat:@"[字典元素为空警告] key is %@, obj is %@", aKey, anObject];
        NSLog(@"%@",reason);
    }
}

- (void)ad_removeObjectForKey:(id)aKey {
    if (aKey) {
        [self removeObjectForKey:aKey];
    } else {
        NSString *reason = [NSString stringWithFormat:@"[字典Key为空警告] key is %@.",aKey];
        NSLog(@"%@",reason);
    }
}

@end

@implementation NSCache (ADSafe)

- (void)ad_setObject:(id)obj forKey:(id)key {
    if (obj && key) {
        [self setObject:obj forKey:key];
    } else {
        NSString *reason = [NSString stringWithFormat:@"[NSCache为空警告] key is %@, obj is %@",key, obj];
        NSLog(@"%@",reason);
    }
}

@end

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"

#pragma mark - NSObject

XXStaticHookClass(NSObject, ProtectFW, id, @selector(forwardingTargetForSelector:), (SEL)aSelector) {
    static struct dl_info app_info;
    if (app_info.dli_saddr == NULL) {
        dladdr((__bridge void *)[UIApplication.sharedApplication.delegate class], &app_info);
    }
    struct dl_info self_info;
    dladdr((__bridge void *)[self class], &self_info);
    
    //  ignore system class
    if (strcmp(app_info.dli_fname, self_info.dli_fname) && !strstr(self_info.dli_fname, "ADKit")) {
        return XXHookOrgin(aSelector);
    }
    
    if ([self isKindOfClass:[NSNumber class]] && [NSString instancesRespondToSelector:aSelector]) {
        NSNumber *number = (NSNumber *)self;
        NSString *str = [number stringValue];
        return str;
    } else if ([self isKindOfClass:[NSString class]] && [NSNumber instancesRespondToSelector:aSelector]) {
        NSString *str = (NSString *)self;
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
        NSNumber *number = [formatter numberFromString:str];
        return number;
    }
    
    BOOL aBool = [self respondsToSelector:aSelector];
    NSMethodSignature *signatrue = [self methodSignatureForSelector:aSelector];
    
    if (aBool || signatrue) {
        return XXHookOrgin(aSelector);
    } else {
        ADStubObject *stub = [ADStubObject shareInstance];
        [stub addFunc:aSelector];
        NSString *reason = [NSString stringWithFormat:@"[方法没有找到] target is %@ method is %@", [self class], NSStringFromSelector(aSelector)];
        NSLog(@"%@",reason);
        return stub;
    }
}
XXStaticHookEnd

XXStaticHookMetaClass(NSObject, ProtectFW, id, @selector(forwardingTargetForSelector:), (SEL)aSelector) {
    BOOL aBool = [self respondsToSelector:aSelector];
    
    NSMethodSignature *signatrue = [self methodSignatureForSelector:aSelector];
    
    if (aBool || signatrue) {
        return XXHookOrgin(aSelector);
    } else {
        [ADStubObject addClassFunc:aSelector];
        NSString *reason = [NSString stringWithFormat:@"[方法没有找到] target is %@ method is %@",[self class], NSStringFromSelector(aSelector) ];
        NSLog(@"%@",reason);
        return [ADStubObject class];
    }
}
XXStaticHookEnd

#pragma mark - NSArray

XXStaticHookPrivateClass(__NSArrayI, NSArray *, ProtectCont, id, @selector(objectAtIndexedSubscript:), (NSUInteger)index) {
    if (index >= self.count) {
        NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@", @(index), @(self.count)];
        NSLog(@"%@",reason);
        return nil;
    }
    return XXHookOrgin(index);
}
XXStaticHookEnd

XXStaticHookPrivateClass(__NSArrayI, NSArray *, ProtectCont, id, @selector(objectAtIndex:), (NSUInteger)index) {
    if (index >= self.count) {
        NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@",@(index), @(self.count)];
        NSLog(@"%@",reason);
        return nil;
    }
    return XXHookOrgin(index);
}
XXStaticHookEnd

XXStaticHookPrivateClass(__NSArray0, NSArray *, ProtectCont, id, @selector(objectAtIndex:), (NSUInteger)index) {
    if (index >= self.count) {
        NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@", @(index), @(self.count)];
        NSLog(@"%@",reason);
        return nil;
    }
    return XXHookOrgin(index);
}
XXStaticHookEnd

XXStaticHookPrivateClass(__NSSingleObjectArrayI, NSArray *, ProtectCont, id, @selector(objectAtIndex:), (NSUInteger)index) {
    if (index >= self.count) {
        NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@", @(index), @(self.count)];
        NSLog(@"%@",reason);
        return nil;
    }
    return XXHookOrgin(index);
}
XXStaticHookEnd

XXStaticHookPrivateClass(__NSPlaceholderArray, NSArray *, ProtectCont, NSArray *, @selector(initWithObjects:count:),
                         (const id *)objects, (NSUInteger)cnt) {
    NSInteger newObjsIndex = 0;
    id  _Nonnull __unsafe_unretained newObjects[cnt];
    for (int i = 0; i < cnt; i++) {
        
        id objc = objects[i];
        if (objc == nil) {
            NSString *reason = [NSString stringWithFormat:@"[数组元素为空警告] obj is %@", objc];
            NSLog(@"%@",reason);
            continue;
        }
        newObjects[newObjsIndex++] = objc;
    }
    return XXHookOrgin(newObjects, newObjsIndex);
}
XXStaticHookEnd

XXStaticHookPrivateClass(__NSArrayM, NSMutableArray *, ProtectCont, id, @selector(objectAtIndexedSubscript:), (NSUInteger)index) {
    if (index >= self.count) {
        NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@",@(index), @(self.count)];
        NSLog(@"%@",reason);
        return nil;
    }
    return XXHookOrgin(index);
}
XXStaticHookEnd

XXStaticHookPrivateClass(__NSArrayM, NSMutableArray *, ProtectCont, void, @selector(setObject:atIndexedSubscript:), (id) obj, (NSUInteger) idx) {
    if (obj) {
        if (idx >= self.count) {
            NSString *reason = [NSString stringWithFormat:@"[数组越界警告] index %@ out of count %@",@(idx), @(self.count)];
            NSLog(@"%@",reason);
        } else {
            XXHookOrgin(obj, idx);
        }
    } else {
        NSString *reason = [NSString stringWithFormat:@"[数组元素为空警告] obj is %@", obj];
        NSLog(@"%@",reason);
    }
}
XXStaticHookEnd

#pragma mark - NSDictionary

XXStaticHookPrivateClass(__NSPlaceholderDictionary, NSDictionary *, ProtectCont, id, @selector(initWithObjects:forKeys:count:),
                      (const id *) objects, (const id<NSCopying> *) keys, (NSUInteger)cnt) {
    NSUInteger index = 0;
    id  _Nonnull __unsafe_unretained newObjects[cnt];
    id  _Nonnull __unsafe_unretained newkeys[cnt];
    for (int i = 0; i < cnt; i++) {
        id tmpItem = objects[i];
        id tmpKey = keys[i];
        if (tmpItem == nil || tmpKey == nil) {
            NSString *reason = [NSString stringWithFormat:@"[字典元素为空警告] key is %@, obj is %@", tmpKey, tmpItem];
            NSLog(@"%@",reason);
            continue;
        }
        newObjects[index] = objects[i];
        newkeys[index] = keys[i];
        index++;
    }
    
    return XXHookOrgin(newObjects, newkeys,index);
}
XXStaticHookEnd

XXStaticHookPrivateClass(__NSDictionaryM, NSMutableDictionary *, ProtectCont, void, @selector(setObject:forKeyedSubscript:), (id)anObject, (id<NSCopying>)aKey ) {
    if (anObject && aKey) {
        XXHookOrgin(anObject,aKey);
    } else {
        NSString *reason = [NSString stringWithFormat:@"[字典元素为空警告] key is %@, obj is %@", aKey, anObject];
        NSLog(@"%@",reason);
    }
}
XXStaticHookEnd

#pragma clang diagnostic pop
